#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/if_packet.h>

#define SERVER_IP "8.8.8.8" // Google DNS服务器地址
#define SERVER_PORT 53 // DNS服务器端口号
#define BUFFER_SIZE 1024 // 缓冲区大小
#define BUFFER_MAX 2048
#define ICMP_DATA_SIZE	56
#define ICMP_4_SIZE		sizeof(struct icmphdr)+ICMP_DATA_SIZE
#define PING_4_SIZE		ICMP_4_SIZE+sizeof(struct iphdr)

uint16_t get_checksum(uint16_t *ptr, int length)
{
	int ret = 0;
	int i;
	for (i = 0; i < (length >> 1); ++ i, ++ ptr)
		ret += *ptr;
	while (ret >> 16)
		ret = (ret & 0xffff) + (ret >> 16);
	return (~ret);
}


static void send_ICMP(int sock_fd)
{
    struct sockaddr_in	addr = {0};
    uint8_t send_buffer[BUFFER_MAX];
    int send_frame = 0;
	struct icmphdr *Frame = (struct icmphdr *)send_buffer;
	Frame->type = ICMP_ECHO;
	Frame->code = 0;
	Frame->checksum = 0;
	Frame->un.echo.id = htons(getpid());
	Frame->un.echo.sequence = htons(++send_frame);


	gettimeofday((struct timeval *)(send_buffer + sizeof(struct icmphdr)), NULL);

	int i;
	uint8_t *ptr = send_buffer + sizeof(struct icmphdr) + sizeof(struct timeval);
	for (i = sizeof(struct timeval); i < ICMP_DATA_SIZE; ++ i)
		*ptr = rand() % 10;

	Frame->checksum = get_checksum((uint16_t *)send_buffer, ICMP_4_SIZE);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);

	if (sendto(sock_fd, send_buffer, ICMP_4_SIZE, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		fprintf(stderr, "Send Error!\n");
		exit(-1);
	}
}
// 测试网速函数
double test_network_speed(char* ifname) {
   struct timeval start, end;
    int sockfd;
    char buffer[BUFFER_SIZE];
    double speed;
    ssize_t ntotal = 0;
    ssize_t nread;
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
        perror("ioctl error");
        exit(EXIT_FAILURE);
    }
    if (!(ifr.ifr_flags & IFF_UP)) {
        printf("%s is not up\n", ifname);
        exit(EXIT_FAILURE);
    }
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr)) < 0) {
        perror("setsockopt error");
        exit(EXIT_FAILURE);
    }
    send_ICMP(sockfd);
    gettimeofday(&start, NULL);
    while ((nread = read(sockfd, buffer, BUFFER_SIZE)) > 0) {
        ntotal += nread;
    }
    gettimeofday(&end, NULL);
    speed = ntotal / ((end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000000.0);
    close(sockfd);
    return speed;
}
// 测试网络状态函数
int test_network_status(char* ifname) {
    struct hostent* host;
    struct sockaddr_in server_addr;
    struct timeval timeout;
    int sockfd, ret;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr)) < 0) {
        perror("setsockopt error");
        exit(EXIT_FAILURE);
    }

    timeout.tv_sec = 3;
    timeout.tv_usec = 0;
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
        perror("setsockopt error");
        exit(EXIT_FAILURE);
    }
    host = gethostbyname(SERVER_IP);
    if (host == NULL) {
        printf("gethostbyname error\n");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr = *((struct in_addr*)host->h_addr);
    ret = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    close(sockfd);
    if (ret < 0) {
        printf("network status: disconnected\n");
        return 0;
    } else {
        printf("network status: connected\n");
        return 1;
    }
}
int main() {
    char* ifname = "ens33"; // 指定网卡名
    double speed;
    int status;
    speed = test_network_speed(ifname);
    printf("network speed: %.2f bytes/s\n", speed);
    status = test_network_status(ifname);
    return 0;
}